﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Linq.Expressions;

namespace Calculator
{

    /// <summary>
    /// Form1クラスの概要
    /// </summary>
    public partial class Form1 : Form
    {
        /// <summary>
        /// Form　コンストラクタ
        /// </summary>
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// スタッククラスの宣言
        /// </summary>
        Stack stack;

        /// <summary>
        /// Form1 ロード
        /// </summary>
        /// <param name="sender">フォームオブジェクト呼び出し</param>
        /// <param name="e">イベント処理に必要なデータ</param>
        private void Form1_Load(object sender, EventArgs e)
        {
            stack = new Stack();

            Btn0.Click += NumButton_Click;             // 0
            Btn00.Click += NumButton_Click;            // 00
            Btn1.Click += NumButton_Click;             // 1
            Btn2.Click += NumButton_Click;             // 2
            Btn3.Click += NumButton_Click;             // 3
            Btn4.Click += NumButton_Click;             // 4
            Btn5.Click += NumButton_Click;             // 5
            Btn6.Click += NumButton_Click;             // 6
            Btn7.Click += NumButton_Click;             // 7
            Btn8.Click += NumButton_Click;             // 8
            Btn9.Click += NumButton_Click;             // 9
            BtnPlus.Click += CalButton_Click;          // +
            BtnMinus.Click += CalButton_Click;         // -
            BtnMultiplied.Click += CalButton_Click;    // *
            BtnDivided.Click += CalButton_Click;       // /
            BtnPoint.Click += CalButton_Click;         // .
            BtnResult.Click += CalButton_Click;        // =
            BtnClear.Click += ClearButton_Click;       // C
            BtnClearEnd.Click += ClearButton_Click;    // CE

        }

        /// <summary>
        /// 画面クローズ時イベント処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {

            Btn0.Click -= NumButton_Click;
            Btn00.Click -= NumButton_Click;
            Btn1.Click -= NumButton_Click;
            Btn2.Click -= NumButton_Click;
            Btn3.Click -= NumButton_Click;
            Btn4.Click -= NumButton_Click;
            Btn5.Click -= NumButton_Click;
            Btn6.Click -= NumButton_Click;
            Btn7.Click -= NumButton_Click;
            Btn8.Click -= NumButton_Click;
            Btn9.Click -= NumButton_Click;
            BtnPlus.Click -= CalButton_Click;
            BtnMinus.Click -= CalButton_Click;
            BtnMultiplied.Click -= CalButton_Click;
            BtnDivided.Click -= CalButton_Click;
            BtnPoint.Click -= CalButton_Click;
            BtnResult.Click -= CalButton_Click;
            BtnClear.Click -= ClearButton_Click;
            BtnClearEnd.Click -= ClearButton_Click;

        }


        /// <summary>
        /// 初期化フラグ
        /// </summary>
        private bool initializes = false;
        /// <summary>
        /// 小数点
        /// </summary>
        private const string POINT = ".";
        /// <summary>
        /// 入力値0
        /// </summary>
        private const string ZERO = "0";


        /// <summary>
        /// 数値ボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NumButton_Click(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            const string ERROR_MESSAGE = "E";

            //初期化する
            if (initializes == true)
            {
                if (sender.Equals(BtnPoint))
                {
                    if (string.IsNullOrEmpty(this.TxtBxOutputNumber.Text)) { return; }
                    if (this.TxtBxOutputNumber.Text.Contains(POINT)) { return; }
                    this.TxtBxOutputNumber.Text += BtnPoint.Text;
                }
                else
                {
                    //テキストボックスが空でないとき
                    if (!(string.IsNullOrEmpty(this.TxtBxOutputNumber.Text))|| this.TxtBxOutputNumber.Text.Contains(ERROR_MESSAGE))
                    {
                        //計算結果上書き
                        this.TxtBxOutputNumber.Text = btn.Text;
                    }
                    else
                    {
                        //続けて出力
                        this.TxtBxOutputNumber.Text += btn.Text;
                    }
                }

                //初期化フラグ
                initializes = false;
                return;
                  
            }

            
            string outputTxt 
                = string.IsNullOrEmpty(this.TxtBxOutputNumber.Text) ? string.Empty : this.TxtBxOutputNumber.Text.Trim();
            
            // 小数点が含まれていなくかつテキストボックスの中身が11より大きく、またはテキストボックスが10より大きい場合なにもしない
            if (!((outputTxt.Contains(POINT)) && outputTxt.Length <= 10 || outputTxt.Length <= 9)) { return; }
            
            
            if (sender.Equals(Btn0))        //0ボタン
            {
                // テキストボックスの中身に0が入っている場合なにもしない
                if (this.TxtBxOutputNumber.Text == ZERO) { return; }
                this.TxtBxOutputNumber.Text += Btn0.Text;

            }
            else if (sender.Equals(Btn00))  //00ボタン
            {
                // テキストボックスの中身が何もない時またはゼロの時
                if (string.IsNullOrEmpty(this.TxtBxOutputNumber.Text) || this.TxtBxOutputNumber.Text.Equals(ZERO)) { return; }
                // テキストボックスの中身が8以下の時なにもしない
                if (outputTxt.Length > 8) { return; }
                this.TxtBxOutputNumber.Text += Btn00.Text;
            }
            else if (sender.Equals(Btn1))   //1ボタン
            {
                // 文字列判定
                SetNumber(Btn1.Text);
            }
            else if (sender.Equals(Btn2))   //2ボタン
            {
                SetNumber(Btn2.Text);
            }
            else if (sender.Equals(Btn3))   //3ボタン
            {
                SetNumber(Btn3.Text);
            }
            else if (sender.Equals(Btn4))   //4ボタン
            {
                SetNumber(Btn4.Text);
            }
            else if (sender.Equals(Btn5))   //5ボタン
            {
                SetNumber(Btn5.Text);
            }
            else if (sender.Equals(Btn6))   //6ボタン
            {
                SetNumber(Btn6.Text);
            }
            else if (sender.Equals(Btn7))   //7ボタン
            {
                SetNumber(Btn7.Text);
            }
            else if (sender.Equals(Btn8))   //8ボタン
            {
                SetNumber(Btn8.Text);
            }
            else if (sender.Equals(Btn9))   //9ボタン
            {
                SetNumber(Btn9.Text);
            }
            else if (sender.Equals(BtnPoint))//Pointボタン
            {
                if (string.IsNullOrEmpty(this.TxtBxOutputNumber.Text) || this.TxtBxOutputNumber.Text.Contains(POINT)) { return; }
                this.TxtBxOutputNumber.Text += BtnPoint.Text;
            }
        }//数値ボタン終了


        /// <summary>
        /// キーボード入力
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NumKey_Up(object sender, KeyEventArgs e)
        {
            // コントロール名が長いのでソース上に何度も出てくると見づらい。テキストのみ欲しいので、
            // string型の変数に格納して使う。
            // string型の変数の場合、nullだとサイズを見たりする時にNullPointerExceptionが発生するので、
            // 空文字を格納する。
            // 判定結果によって変数に値を格納する場合、下記のような記述をすることが出来る。
            // [格納する変数] = (判定式) ? (判定式が真の場合の値) : (判定式が偽の場合の値)
            string outputText
                = String.IsNullOrEmpty(this.TxtBxOutputNumber.Text) ? String.Empty : this.TxtBxOutputNumber.Text.Trim();

            // バックスペース
            if (e.KeyCode == Keys.Back)
            {
                if (String.IsNullOrEmpty(outputText)) { return; }
                this.TxtBxOutputNumber.Text = outputText.Remove(outputText.Length - 1);

                // 以降の処理は必要ないので、処理を終了する
                // ※ もしここで処理を終えない場合は、不必要な判定分をすべてチェックしていくようになる
                this.TxtBxOutputNumber.SelectionStart = this.TxtBxOutputNumber.Text.Length;
                return;
            }

            // 入力時のテキストボックスが、小数点を含む状態で10文字以下の場合、
            // または、9文字以下でない場合、のどちらの条件にも当てはまらない場合は処理を中断する
            if (!((outputText.Contains(POINT) && outputText.Length <= 10)
                || (outputText.Length <= 9))) { return; }

            // 入力キー
            switch (e.KeyCode)
            {
                case Keys.D0:		// 0キーの場合
                case Keys.NumPad0:	// テンキーの0の場合
                    // 0を続けて入力しない
                    if (outputText.Equals(ZERO)) { return; }
                    this.TxtBxOutputNumber.Text += Btn0.Text;
                    break;
                case Keys.D1:		// 1キーの場合
                case Keys.NumPad1:	// テンキーの1の場合
                    SetNumber(Btn1.Text);
                    break;
                case Keys.D2:		// 2キーの場合
                case Keys.NumPad2:	// テンキーの2の場合
                    SetNumber(Btn2.Text);
                    break;
                case Keys.D3:		// 3キーの場合
                case Keys.NumPad3:	// テンキーの3の場合
                    SetNumber(Btn3.Text);
                    break;
                case Keys.D4:		// 4キーの場合
                case Keys.NumPad4:	// テンキーの4の場合
                    SetNumber(Btn4.Text);
                    break;
                case Keys.D5:		// 5キーの場合
                case Keys.NumPad5:	// テンキーの5の場合
                    SetNumber(Btn5.Text);
                    break;
                case Keys.D6:		// 6キーの場合
                case Keys.NumPad6:	// テンキーの6の場合
                    SetNumber(Btn6.Text);
                    break;
                case Keys.D7:		// 7キーの場合
                case Keys.NumPad7:	// テンキーの7の場合
                    SetNumber(Btn7.Text);
                    break;
                case Keys.D8:		// 8キーの場合
                case Keys.NumPad8:	// テンキーの8の場合
                    SetNumber(Btn8.Text);
                    break;
                case Keys.D9:		// 9キーの場合
                case Keys.NumPad9:	// テンキーの9の場合
                    SetNumber(Btn9.Text);
                    break;
                case Keys.OemPeriod:	// ピリオドキーの場合
                case Keys.Decimal:		// 小数点キーの場合
                    // 未入力状態の場合、または既に小数点が含まれている場合
                    if (String.IsNullOrEmpty(outputText) || outputText.Contains(POINT)) { return; }
                    this.TxtBxOutputNumber.Text += BtnPoint.Text;
                    break;
            }

            this.TxtBxOutputNumber.SelectionStart = this.TxtBxOutputNumber.Text.Length;
        }


        /// <summary>
        /// TxtBxOutputNumberに数字文字列を設定する
        /// </summary>
        /// <param name="numberText">設定する数字文字列</param>
        private void SetNumber(string numberText)
        {
            // テキストボックスのテキストが"0"の場合は、"0"を消して数字文字列を設定する
            if (this.TxtBxOutputNumber.Text.Equals(ZERO))
            {
                this.TxtBxOutputNumber.Text = numberText;
            }
            // テキストボックスが"0"でない場合は、続けて入力する
            else
            {
                this.TxtBxOutputNumber.Text += numberText;
            }
        }


        /// <summary>
        /// 演算子ボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CalButton_Click(object sender, EventArgs e)
        {

            const string PLUS = "+";
            const string MINUS = "-";
            const string MULTI = "*";
            const string DIVID = "/";
            string endResult = "";
            double input;


            if (sender.Equals(BtnPlus))             //PLUSボタン
            {
                this.TxtBxCheck(PLUS);
            }
            else if (sender.Equals(BtnMinus))       //MINUSボタン
            {
                //入力値に何も入っていない場合
                if (string.IsNullOrEmpty(this.TxtBxOutputNumber.Text))
                {
                    //テキストボックスにマイナスを表示する
                    this.TxtBxOutputNumber.Text += BtnMinus.Text;
                }
                //何か入っている場合
                else
                {
                    this.TxtBxCheck(MINUS);
                }
            }
            else if (sender.Equals(BtnMultiplied))  //MULTIボタン
            {
                this.TxtBxCheck(MULTI);
            }
            else if (sender.Equals(BtnDivided))     //DIVIDボタン
            {
                this.TxtBxCheck(DIVID);
            }
            else if (sender.Equals(BtnResult))      //RESULTボタン
            {

                // スタックが0の時
                if (stack.NullStack() == true) { return; }


                //double型に変換可能なら
                if (double.TryParse(this.TxtBxOutputNumber.Text, out input))
                {
                    //スタックの頂上を入力値とし各計算を行う
                    endResult = stack.OutPut(input);
                }
                //double型に変換不可能なら
                else
                {
                    //スタックの頂上を0とし各計算を行う
                    endResult = stack.OutPut(0);
                }

                //計算式を削除して、計算結果を出力。
                this.TxtBxCalculation.Clear();
                this.TxtBxOutputNumber.Text = endResult;

                //初期化フラグ
                initializes = true;
                //スタックの中身を空にする。
                stack.ClearStack();
            }
        }


        /// <summary>
        /// テキストボックスの中身がdouble型に変換できるかどうか
        /// </summary>
        /// <param name="inputOper">演算子</param>
        private void TxtBxCheck(string inputOper)
        {
            //変換可能時double型の数値として格納
            double input;
            //計算式を格納
            string print = "";
            const string ERROE = "E";


            //もし入力値が変換可能なら
            if (double.TryParse(this.TxtBxOutputNumber.Text, out input))
            {
                //Castメソッドで計算を行う
                 string rtnString =stack.Cast(input, inputOper);
                 if (rtnString == ERROE)
                 {
                     this.TxtBxOutputNumber.Text = rtnString;
                     this.TxtBxCalculation.Clear();
                     stack.ClearStack();
                     initializes = true;
                     return;
                 }
                 else
                 {
                     //計算式出力
                     print = stack.TxtBxPrint(this.TxtBxCalculation.Text);
                     this.TxtBxCalculation.Text = print;
                 }
            }
            else
            {
                //空の場合処理しない
                if (stack.NullStack() == true) { return; }

                //押下された演算子にちなんで入れ替え
                stack.ChangeOper(inputOper);

                //計算式出力
                print = stack.TxtBxPrint(this.TxtBxCalculation.Text);
                this.TxtBxCalculation.Text = print;
            }

            this.TxtBxOutputNumber.Clear();
        }


        /// <summary>
        /// クリアボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClearButton_Click(object sender, EventArgs e)
        {

            //クリアボタン押下
            if (sender.Equals(BtnClear))
            {
                //テキストボックスの中身を削除
                this.TxtBxOutputNumber.Clear();
                this.TxtBxCalculation.Clear();
                //スタックを初期化
                stack.ClearStack();

            }//クリアボタン終了


            //クリアエンド押下
            else if (sender.Equals(BtnClearEnd))
            {
                string print;

                //スタックが空の時またはテキストボックスに何か入っている時
                if (stack.NullStack() == true || !(string.IsNullOrEmpty(this.TxtBxOutputNumber.Text)))
                {
                    //入力値を消す。
                    this.TxtBxOutputNumber.Clear();
                    return;
                }
                else
                //テキストボックスの中身に値が入っていない場合
                {
                    //クリアエンドスタックの処理を行う
                    stack.ClearEndStack();

                    //文字出力
                    print = stack.TxtBxPrint(this.TxtBxCalculation.Text);
                    this.TxtBxCalculation.Text = print;

                }

            }
        }
    }
}
